import createDOMString from '../../utils/createDOMString';
import CMSElement from '../../utils/CMSElement';
import { createPopper } from '@popperjs/core';


class CmsTooltips extends CMSElement {
  //用于存储标签属性
  props = {
    //tooltip弹窗位置,同element的placement属性
    placement: 'bottom',
    //暂未实现
    strategy: 'absolute',
    //暂未实现
    enabled: 'false',
    //tooltip弹窗偏移量
    offsetX: '0',
    offsetY: '0',
    //是否展示箭头
    arrow: 'true',
    //主题颜色：dark/light
    theme: 'dark',
    //是否显示tooltip弹窗
    show: 'true',
    //弹窗触发方式：hover/click/focus/static
    trigger: 'hover',
    //填充内容，tooltip内填充的展示内容
    content: 'I am a tooltip',
    //强制展示时长（click触发方式不区分displaytime和delaytime）
    displaytime: '',
    //退出事件后的继续展示时长,此属性对static不生效
    delaytime: '1000',
    //
  };
  constructor() {
    super();
    this._sd = this.attachShadow({ mode: 'closed' });
    this.renderDom();
    this.renderStyle();
    this.addListener();
  }
  timeoutId = '';
  //用于添加tooltip弹窗dom元素的函数
  addNodeFnc() {
    const template = document.createElement('template');
    template.innerHTML = `
        <div id="cms-tooltip" role="tooltip">${this.props.content}<span id="arrow"></span></div>
      `;
    this._sd.appendChild(template.content.cloneNode(true));
    this.bindPopper();
  }
  //用于移除tooltip弹窗dom元素的函数
  removeNodeFnc() {
    const tooltip = this._sd.querySelector('#cms-tooltip');
    const arrow = this._sd.querySelector('#arrow');
    if (tooltip) {
      tooltip.remove();
    }
    if (arrow) {
      arrow.remove();
    }
  }
  //点击事件回调函数
  clickFnc() {
    //注销点击事件，setTimeout结束后再次注册
    if (this.props.displaytime) {
      this.fnc.addNodeFncBind();
      this.bindPopper();
      window.setTimeout(() => {
        this.fnc.removeNodeFncBind();
        this.addListener('click');
      }, Number(this.props.displaytime));
      this.removeListener('click');
    } else if (this.props.delaytime) {
      this.fnc.removeNodeFncBind();
      this.fnc.addNodeFncBind();
      this.bindPopper();
      if (this.timeoutId) clearTimeout(this.timeoutId);
      this.timeoutId = window.setTimeout(() => {
        this.fnc.removeNodeFncBind();
      }, Number(this.props.delaytime));
    }
  }
  //mouseenter事件回调函数
  mouseenterFnc() {
    this.fnc.addNodeFncBind();
    this.bindPopper();
    const container = this.querySelector('*');
    if (this.props.displaytime) {
      this.removeListener('hover');
      window.setTimeout(() => {
        this.fnc.removeNodeFncBind();
        this.addListener('hover');
      }, Number(this.props.displaytime));
    } else {
      container.addEventListener('mouseleave', this.fnc.mouseleaveFncBind);
    }
  }
  //mouseleave事件回调函数
  mouseleaveFnc() {
    this.removeListener('hover');
    window.setTimeout(() => {
      this.fnc.removeNodeFncBind();
      this.addListener('hover');
    }, Number(this.props.delaytime));
  }
  //focus事件回调函数
  focusFnc() {
    this.fnc.addNodeFncBind();
    this.bindPopper();
    const container = this.querySelector('*');
    if (this.props.displaytime) {
      this.removeListener('focus');
      window.setTimeout(() => {
        this.fnc.removeNodeFncBind();
        this.addListener('focus');
      }, Number(this.props.displaytime));
    } else {
      container.addEventListener('blur', this.fnc.blurFncBind);
    }
  }
  //blur事件回调函数
  blurFnc() {
    this.removeListener('focus');
    window.setTimeout(() => {
      this.fnc.removeNodeFncBind();
      this.addListener('focus');
    }, Number(this.props.delaytime));
  }
  //改变各类事件回调函数的指向，确保触发事件后this指向当前组件而不是触发事件的对象
  fnc = {
    addNodeFncBind: this.addNodeFnc.bind(this),
    removeNodeFncBind: this.removeNodeFnc.bind(this),
    clickFncBind: this.clickFnc.bind(this),
    mouseenterFncBind: this.mouseenterFnc.bind(this),
    focusFncBind: this.focusFnc.bind(this),
    blurFncBind: this.blurFnc.bind(this),
    mouseleaveFncBind: this.mouseleaveFnc.bind(this),
  };
  //移除所有事件监听器
  removeListener(last) {
    last = last ? last : 'hover';
    const container = this.querySelector('*');
    switch (last) {
      case 'hover':
        container.removeEventListener('mouseenter', this.fnc.mouseenterFncBind);
        container.removeEventListener('mouseleave', this.fnc.mouseleaveFncBind);
        break;
      case 'click':
        container.removeEventListener('click', this.fnc.clickFncBind);
        break;
      case 'focus':
        container.removeEventListener('focus', this.fnc.focusFncBind);
        container.removeEventListener('blur', this.fnc.blurFncBind);
        break;
      case 'static':
        window.setTimeout(() => {
          this.fnc.removeNodeFncBind();
        }, Number(this.props.displaytime));
        break;
    }
  }
  //添加事件监听器
  addListener(type) {
    const container = this.querySelector('*');
    switch (type ? type : this.props.trigger) {
      case 'hover':
        container.addEventListener('mouseenter', this.fnc.mouseenterFncBind);
        break;
      case 'click':
        container.addEventListener('click', this.fnc.clickFncBind);
        break;
      case 'focus':
        container.addEventListener('focus', this.fnc.focusFncBind);
        break;
      case 'static':
        this.fnc.addNodeFncBind();
        this.bindPopper();
        break;
    }
  }
  //渲染基础dom元素结构
  renderDom() {
    const template = document.createElement('template');
    template.innerHTML = '<slot></slot>';
    this._sd.appendChild(template.content.cloneNode(true));
  }
  //使用popper.js工具，实现tooltip定位
  bindPopper() {
    this.renderStyle();
    const slotElement = this.querySelector('*');
    const tooltip = this._sd.querySelector('#cms-tooltip');
    createPopper(slotElement, tooltip, {
      placement: this.props.placement,
      strategy: this.props.strategy,
      modifiers: [
        {
          name: 'offset',
          options: {
            offset: [Number(this.props.offsetY), Number(this.props.offsetX)],
          },
        },
        // {
        //   name: 'preventOverflow',
        //   options: {mainAxis: false,},
        // },
      ],
    });
  }
  //渲染或更新样式
  renderStyle() {
    // 保证shadow dom中只存在一个style标签
    let style = this._sd.querySelector('style');
    if (!style) {
      style = document.createElement('style');
      this._sd.appendChild(style);
    }
    const styleProps = {
      theme: this.props.theme,
      placement: this.props.placement,
      ...this.global,
    };
    const getTheme = prop => {
      if (['dark', 'light'].includes(prop.theme)) {
        switch (prop.theme) {
          case 'dark':
            return '#333';
          case 'light':
            return '#fff';
        }
      } else {
        return prop.theme;
      }
    };
    const getColor = prop => {
      if (['dark', 'light'].includes(prop.theme)) {
        switch (prop.theme) {
          case 'dark':
            return '#fff';
          case 'light':
            return '#333';
        }
      } else {
        return prop.theme;
      }
    };
    //动态更改arrow元素朝向
    const getBorderByPlacement = props => {
      let colorA;
      if (props.theme == 'dark') {
        colorA = '#333';
      } else {
        colorA = '#fff';
      }
      const placementHeader = props.placement.split('-');
      const lastValueX = Number(this.props.offsetX);
      const lastValueY = Number(this.props.offsetY);
      if (placementHeader[0] == 'top') {
        this.props.offsetY == '0' ? (this.props.offsetY = `${lastValueY - 5}`) : null;
        return `border-bottom: 0px solid transparent;border-top: 5px solid ${colorA};border-right: 5px solid transparent;border-left: 5px solid transparent;`;
      } else if (placementHeader[0] == 'left') {
        this.props.offsetX == '0' ? (this.props.offsetX = `${lastValueX + 5}`) : null;
        return `border-bottom: 5px solid transparent;border-top: 5px solid transparent;border-right: 0px solid transparent;border-left: 5px solid ${colorA};`;
      } else if (placementHeader[0] == 'bottom') {
        this.props.offsetY == '0' ? (this.props.offsetY = `${lastValueY + 5}`) : null;
        return `border-bottom: 5px solid ${colorA};border-top: 0px solid transparent;border-right: 5px solid transparent;border-left: 5px solid transparent;`;
      } else {
        this.props.offsetX == '0' ? (this.props.offsetX = `${lastValueX - 5}`) : null;
        return `border-bottom: 5px solid transparent;border-top: 5px solid transparent;border-right: 5px solid ${colorA};border-left: 0px solid transparent;`;
      }
    };
    //动态更改arrow元素定位
    const getTopAndLeftValue = props => {
      const tooltip = this._sd.querySelector('#cms-tooltip');
      switch (props.placement) {
        case 'top':
          return `top:${tooltip.clientHeight}px;left:${tooltip.clientWidth / 2 - 5}px;`;
        case 'top-start':
          return `top:${tooltip.clientHeight}px;left:${tooltip.clientWidth - 12}px;`; //2*5(arrow-border)+2(border-radius)
        case 'top-end':
          return `top:${tooltip.clientHeight}px;left:2px;`;
        case 'bottom':
          return `top:-5px;left:${tooltip.clientWidth / 2 - 5}px;`;
        case 'bottom-start':
          return `top:-5px;left:${tooltip.clientWidth - 12}px;`;
        case 'bottom-end':
          return 'top:-5px;left:2px;';
        case 'right':
          return `top:${tooltip.clientHeight / 2 - 5}px;left:-5px;`;
        case 'right-start':
          return 'top:5px;left:-5px;';
        case 'right-end':
          return 'top:10px;left:-5px;';
        case 'left':
          return `top:${tooltip.clientHeight / 2 - 5}px;left:${tooltip.clientWidth}px;`;
        case 'left-start':
          return `top:5px;left:${tooltip.clientWidth}px;`;
        case 'left-end':
          return `top:10px;left:${tooltip.clientWidth}px;`;
      }
    };
    //根据不同props属性来获取不同css值
    const tooltip = this._sd.querySelector('#cms-tooltip');
    style.innerHTML = createDOMString(styleProps)`
        :host {
          width: 100%;
          height: 100%;
        }
        :host> #cms-tooltip {
          ${this.props.show === 'true' ? '' : 'display:none;'}
          background-color: ${getTheme};
          color: ${getColor};
          padding: 5px 10px;
          border-radius: 4px;
          border-color:${getColor};
          font-size: 13px;
          box-shadow: 0 0 9px ${getColor};
        }
        :host> #cms-tooltip> #arrow {
          ${this.props.arrow === 'true' ? '' : 'display:none;'}
          position: absolute;
          width: 0px;
          height: 0px;
          ${tooltip ? getBorderByPlacement : ''}
          ${tooltip ? getTopAndLeftValue : ''}
        }
      `;
  }
  static get observedAttributes() {
    return [
      'content',
      'trigger',
      'show',
      'placement',
      'strategy',
      'enabled',
      'offset-x',
      'offset-y',
      'arrow',
      'theme',
      'displaytime',
      'delaytime',
    ];
  }
  //cms-tooltip属性变化时的回调函数
  attributeChangedCallback(name, last, cur) {
    const placementArr = [
      'top',
      'top-start',
      'top-end',
      'bottom',
      'bottom-start',
      'bottom-end',
      'left',
      'left-start',
      'left-end',
      'right',
      'right-start',
      'right-end',
    ];
    const booleanArr = ['true', 'false'];
    const triggerArr = ['hover', 'click', 'focus', 'static'];
    switch (name) {
      case 'placement':
        this.props.placement = placementArr.includes(cur) ? cur : 'bottom';
        this.bindPopper();
        break;
      case 'strategy':
        this.props.strategy = ['absolute', 'fixed'].includes(cur) ? cur : 'absolute';
        this.bindPopper();
        break;
      case 'enabled':
        this.props.enabled = booleanArr.includes(cur) ? cur : 'false';
        this.bindPopper();
        break;
      case 'offset-x':
        this.props.offsetX = Number(cur) ? cur : '0';
        this.bindPopper();
        break;
      case 'offset-y':
        this.props.offsetY = Number(cur) ? cur : '0';
        this.bindPopper();
        break;
      case 'arrow':
        this.props.arrow = booleanArr.includes(cur) ? cur : 'true';
        this.renderStyle();
        break;
      case 'theme':
        this.props.theme = cur;
        this.renderStyle();
        break;
      case 'show':
        this.props.show = booleanArr.includes(cur) ? cur : 'false';
        this.renderStyle();
        break;
      case 'trigger':
        this.removeListener(last);
        this.fnc.removeNodeFncBind;
        this.props.trigger = triggerArr.includes(cur) ? cur : 'hover';
        this.addListener();
        break;
      case 'content':
        this.props.content = cur;
        break;
      case 'displaytime':
        this.props.displaytime = cur;
        break;
      case 'delaytime':
        this.props.delaytime = cur;
        break;
    }
  }
}
customElements.define('cms-tooltips', CmsTooltips);
